package top.go2do.auth.business.app.service;

import jakarta.persistence.criteria.Predicate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import top.go2do.common.dao.ApplicationRepository;
import top.go2do.auth.business.app.enumeration.AppStatus;
import top.go2do.auth.business.app.module.*;
import top.go2do.common.dao.UserRepository;
import top.go2do.common.module.UserPO;
import top.go2do.auth.exception.AuthErrorCode;
import top.go2do.auth.exception.AuthException;
import top.go2do.common.module.ApplicationPO;
import top.go2do.common.module.pageQuery.PageResult;
import top.go2do.common.utils.MySecureRandomStringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author xingyj
 * @CreateTime 2025/3/17 13:01
 */
@Service
public class ApplicationServiceImpl implements ApplicationService {

    @Autowired
    private ApplicationRepository applicationRepository;

    @Autowired
    private UserRepository userRepository;

    @Override
    public ApplicationPO create(AppCreateForm appCreateForm) {
        String appName = appCreateForm.getAppName();
        Long userId = appCreateForm.getUserId();
        ApplicationPO applicationPO = applicationRepository.findByAppName(appName);
        if (applicationPO != null) {
            throw new AuthException(AuthErrorCode.APP_NAME_EXIST);
        }
        applicationPO = new ApplicationPO();
        applicationPO.setAppName(appName);
        String appId = generateAppId();
        applicationPO.setAppId(appId);
        String appSecret = MySecureRandomStringUtils.generate(32);
        applicationPO.setAppSecret(appSecret);
        applicationPO.setStatus(AppStatus.NORMAL.getValue());
        applicationPO.setCreatedBy(userId);
        applicationPO.setUpdatedBy(userId);
        applicationRepository.save(applicationPO);
        return getById(appId);
    }

    @Override
    public void modifyById(AppModifyForm appModifyForm) {
        ApplicationPO applicationPO = this.getById(appModifyForm.getAppId());
        Integer status = appModifyForm.getStatus();
        String appName = appModifyForm.getAppName();
        if (StringUtils.hasText(appName)) {
            applicationPO.setAppName(appName);
        }
        if (status != null) {
            applicationPO.setStatus(status);
        }
        applicationPO.setUpdatedBy(appModifyForm.getUserId());
        applicationRepository.save(applicationPO);
    }

    @Override
    public void removeById(String id) {
        this.getById(id);
        applicationRepository.deleteById(id);
    }

    @Override
    public ApplicationPO getById(String appId) {
        ApplicationPO applicationPO = applicationRepository.findById(appId).orElse(null);
        if (applicationPO == null) {
            throw new AuthException(AuthErrorCode.APP_ID_NOT_EXIST);
        }
        return applicationPO;
    }

    @Override
    public PageResult<ApplicationPO> pageQuery(AppQueryCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());
        /**
         * pageNumber 从0开始
         */
        Integer page = condition.getPage();
        Integer size = condition.getSize();
        Pageable pageable = PageRequest.of(page, size, sort);

        Specification<ApplicationPO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getAppId() != null) {
                predicates.add(cb.like(root.get("appId"), "%" + condition.getAppId() + "%"));
            }
            if (condition.getAppName() != null) {
                predicates.add(cb.like(root.get("appName"), "%" + condition.getAppName() + "%"));
            }
            if (condition.getStatus() != null) {
                predicates.add(cb.equal(root.get("status"), condition.getStatus()));
            }
            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("appId"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("appName"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        Page<ApplicationPO> queryResult = applicationRepository.findAll(spec, pageable);
        PageResult<ApplicationPO> pageResult = new PageResult(queryResult.getNumber(), queryResult.getSize(), queryResult.getTotalElements(), queryResult.getTotalPages());
        pageResult.setList(queryResult.getContent());
        return pageResult;
    }

    @Override
    public List<ApplicationPO> listAll(AppQueryAllCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());

        Specification<ApplicationPO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getAppId() != null) {
                predicates.add(cb.like(root.get("appId"), "%" + condition.getAppId() + "%"));
            }
            if (condition.getAppName() != null) {
                predicates.add(cb.like(root.get("appName"), "%" + condition.getAppName() + "%"));
            }
            if (condition.getStatus() != null) {
                predicates.add(cb.equal(root.get("status"), condition.getStatus()));
            }
            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("appId"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("appName"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        List<ApplicationPO> list = applicationRepository.findAll(spec, sort);
        return list;
    }

    /**
     * 生成32位的UUID
     *
     * @return
     */
    private String generateAppId() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    @Override
    public List<AppListVO> po2vo(List<ApplicationPO> list) {
        Set<Long> userIdSet = new HashSet<>();
        for (ApplicationPO po : list) {
            userIdSet.add(po.getCreatedBy());
            userIdSet.add(po.getUpdatedBy());
        }
        List<UserPO> userList = userRepository.findAllById(userIdSet);
        Map<Long, String> userId2NameMap;
        if (userList != null) {
            userId2NameMap = userList.stream().collect(Collectors.toMap(UserPO::getUserId, UserPO::getUsername));
        } else {
            userId2NameMap = new HashMap<>(0);
        }
        List<AppListVO> voList = list.stream().map(po -> {
            AppListVO vo = new AppListVO();
            BeanUtils.copyProperties(po, vo);
            vo.setCreatedByName(userId2NameMap.get(po.getCreatedBy()));
            vo.setUpdatedByName(userId2NameMap.get(po.getUpdatedBy()));
            vo.setUpdatedBy(String.valueOf(po.getUpdatedBy()));
            vo.setCreatedBy(String.valueOf(po.getCreatedBy()));
            return vo;
        }).toList();
        return voList;
    }
}
